﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint.Administration;
using System.Xml;
using Microsoft.SharePoint;
using System.Collections.ObjectModel;
using System.IO;
using System.Text.RegularExpressions;
using System.Reflection;
using System.Collections;

namespace Wilke.SharePoint.Tools
{
    internal static class SharePointUtilites
    {
        #region  Private Methods

        private static SPWebApplication GetWebApplication(string webApplicationName)
        {
            SPWebApplication webApplication = null;

            try
            {
                SPWebService webService = SPWebService.ContentService;
                webApplication = webService.WebApplications[webApplicationName];
            }
            catch (Exception)
            {
                throw;
            }

            return webApplication;
        }

        #endregion

        internal static List<string> GetWebApplications()
        {
            List<string> webApplications = new List<string>();

            try
            {
                SPWebService webService = SPWebService.ContentService;

                if (webService != null)
                {
                    foreach (SPWebApplication webApplication in webService.WebApplications)
                    {   
                        if (!webApplication.IsAdministrationWebApplication)
                        {
                            webApplications.Add(webApplication.Name);
                        }
                    }
                }
                else
                {
                    throw new UnauthorizedAccessException("Could not access SharePoint 'SPWebService'");
                }
            }
            catch
            {
                throw;
            }

            return webApplications;
        }

        internal static List<WebConfigModification> GetWebConfigModifications(string webApplicationName)
        {
            List<WebConfigModification> webConfigModifications = new List<WebConfigModification>();

            try
            {
                #region Retrieve SPWebConfigFileChanges

                //We do not use the WebConfigModifications property of the WebApplication since it does not keep track of modifications added as EnsureSection.
                //If you clear the WebConfigModifications property of the WebApplication it will have no more entries, the SPWebCOnfigFileChanges on the other
                //hand will still contain modifications added as EnsureSection.
                //Since SPWebConfigFileChanges is an internal class in the Microsoft.SharePoint assembly we will need some reflection to retrieve these values.
                //I could of course fall back on invoking a SQL query on the configuration database but this could require changing the permissions on the configuration
                //database but this application tries to mimic what SharePoint is doing as close as possible.

                SPWebApplication webApplication = GetWebApplication(webApplicationName);

                string objectName = String.Format("WebConfigChanges - {0}", SPServer.Local.Id);

                Assembly assembly = Assembly.GetAssembly(typeof(SPWebApplication));

                Type type = assembly.GetType("Microsoft.SharePoint.Administration.SPWebConfigFileChanges");

                object webConfigFileChanges = SPFarm.Local.GetObject(objectName, webApplication.Id, type);

                List<SPWebConfigModification> modifications = new List<SPWebConfigModification>();

                SortedList sortedList = null;

                sortedList = (SortedList)webConfigFileChanges.GetType().GetField("WebConfigAttrChanges", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(webConfigFileChanges);

                PopulateModificationList(modifications, sortedList);

                sortedList = (SortedList)webConfigFileChanges.GetType().GetField("WebConfigChildNodes", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(webConfigFileChanges);

                PopulateModificationList(modifications, sortedList);

                sortedList = (SortedList)webConfigFileChanges.GetType().GetField("WebConfigSections", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(webConfigFileChanges);

                PopulateModificationList(modifications, sortedList);
  
                #endregion

                foreach (SPWebConfigModification spWebConfigModification in modifications)
                {
                    WebConfigModification webConfigModification = new Tools.WebConfigModification();
                    webConfigModification.Name = spWebConfigModification.Name;
                    webConfigModification.Owner = spWebConfigModification.Owner;
                    webConfigModification.Path = spWebConfigModification.Path;
                    webConfigModification.Sequence = (int)spWebConfigModification.Sequence;
                    webConfigModification.Type = (WebConfigModificationType)spWebConfigModification.Type;
                    webConfigModification.Value = spWebConfigModification.Value;
                    webConfigModification.Checked = true;
                    webConfigModifications.Add(webConfigModification);
                }
            }
            catch
            {
                throw;
            }

            return webConfigModifications;
        }

        private static void PopulateModificationList(List<SPWebConfigModification> modifications, SortedList sortedList)
        {
            foreach (SPWebConfigModification modification in sortedList.Values)
            {
                modifications.Add(modification);
            }
        }

        /// <summary>
        /// This method retrieves the web.config file for the selected SPWebAppliction.
        /// </summary>
        /// <param name="webApplicationName">The name of the SPWebApplication instance.</param>
        /// <returns>An xml document containing the web.config for the selected SPWebApplication</returns>
        /// <remarks>Currently the web.config for the default zone is retrieved.</remarks>
        internal static XmlDocument GetWebConfigFile(string webApplicationName)
        {
            XmlDocument xmlDocument = null;

            try
            {
                SPWebApplication webApplication = GetWebApplication(webApplicationName);

                SPIisSettings iisSettings = webApplication.IisSettings[SPUrlZone.Default];

                string path = iisSettings.Path.ToString();

                if (!path.EndsWith(@"\"))
                {
                    path = path + @"\";
                }
                path = path + "web.config";

                if (File.Exists(path))
                {
                    xmlDocument = new XmlDocument();
                    xmlDocument.Load(path);
                }
            }
            catch
            {
                throw;
            }

            return xmlDocument;
        }

        internal static bool PreviewWebConfigModifications(ref XmlDocument webConfig, List<WebConfigModification> appliedWebConfigModifications, List<WebConfigModification> toApplyWebConfigModifications)
        {
            bool success = false;
            Collection<WebConfigModification> previousWebConfigModifications = null;
            Collection<WebConfigModification> newWebConfigModifications = null;

            #region Populate WebModification Collections

            if (appliedWebConfigModifications != null && appliedWebConfigModifications.Count > 0)
            {
                previousWebConfigModifications = new Collection<WebConfigModification>();                

                foreach (WebConfigModification webConfigModification in appliedWebConfigModifications)
                { 
                    previousWebConfigModifications.Add(webConfigModification);

                    //Selected for reapply
                    if (webConfigModification.Checked)
                    {
                        if (newWebConfigModifications == null)
                        {
                            newWebConfigModifications = new Collection<WebConfigModification>();
                        }

                        newWebConfigModifications.Add(webConfigModification);
                    }
                }
            }

            if (toApplyWebConfigModifications != null)
            {
                if (newWebConfigModifications == null)
                {
                    newWebConfigModifications = new Collection<WebConfigModification>();
                }

                #region Add New Modifications

                foreach (WebConfigModification webConfigModification in toApplyWebConfigModifications)
                {
                    if (webConfigModification.Checked)
                    { 
                        newWebConfigModifications.Add(webConfigModification);
                    }
                }

                #endregion
            }

            #endregion

            try
            {
                if (previousWebConfigModifications != null)
                {
                    WebConfigFileChanges previousWebConfigFileChanges = new WebConfigFileChanges();
                    previousWebConfigFileChanges.RebuildListModifications(previousWebConfigModifications);
                    previousWebConfigFileChanges.RemoveModificationsWebConfigXmlDocument(webConfig);                    
                }

                if (newWebConfigModifications != null)
                {
                    WebConfigFileChanges newWebConfigFileChanges = new WebConfigFileChanges();
                    newWebConfigFileChanges.RebuildListModifications(newWebConfigModifications);
                    newWebConfigFileChanges.ApplyModificationsWebConfigXmlDocument(webConfig);
                }

                success = true;
            }
            catch (SPException spException)
            {
                throw new SPException(spException.Message);
            }
            catch(Exception exception)
            {
                throw new SPException(exception.Message);
            }

            return success;
        }

        internal static List<WebConfigModification> GetWebConfigModificationsForCodeGeneration(List<WebConfigModification> webConfigModifications, WebConfigModificationAction webConfigModificationAction)
        {
            Collection<WebConfigModification> webConfigModificationsToSort = null;
            List<WebConfigModification> sortedWebConfigModifications = null;
            bool addItem = false;

            #region Populate WebModification Collection

            if (webConfigModifications != null)
            {
                webConfigModificationsToSort = new Collection<WebConfigModification>();

                foreach (WebConfigModification webConfigModification in webConfigModifications)
                {
                    if (webConfigModificationAction == WebConfigModificationAction.Remove)
                    {
                        addItem = !webConfigModification.Checked;
                    }
                    else
                    {
                        addItem = webConfigModification.Checked;
                    }

                    if (addItem)
                    {
                        webConfigModificationsToSort.Add(webConfigModification);                        
                    }
                }
            }

            #endregion

            try
            {
                if (webConfigModificationsToSort != null)
                {
                    WebConfigFileChanges webConfigFileChanges = new WebConfigFileChanges();
                    webConfigFileChanges.RebuildListModifications(webConfigModificationsToSort);
                    sortedWebConfigModifications = new List<WebConfigModification>();

                    foreach (WebConfigModification webConfigModification in webConfigFileChanges.WebConfigSections.Values)
                    {
                        sortedWebConfigModifications.Add(webConfigModification);
                    }
                    foreach (WebConfigModification webConfigModification in webConfigFileChanges.WebConfigChildNodes.Values)
                    {
                        sortedWebConfigModifications.Add(webConfigModification);
                    }
                    foreach (WebConfigModification webConfigModification in webConfigFileChanges.WebConfigAttrChanges.Values)
                    {
                        sortedWebConfigModifications.Add(webConfigModification);
                    }
                }
            }
            catch (SPException spException)
            {
                throw new SPException(spException.Message);
            }
            catch (Exception exception)
            {
                throw new SPException(exception.Message);
            }

            return sortedWebConfigModifications;
        }
    }
}
